package com.elite.xml.config;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileUrlResource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.support.TransactionTemplate;

import javax.sql.DataSource;
import java.net.MalformedURLException;


/**
 * Spring 整合 mybatis 原理：
 *  MapperScannerConfigurer：
 *  	1. 创建 MapperScannerConfigurer bean 对象 指定 Mapper 包路径，它扫描 Mapper 包路径 最后生成 一个个的 bean的定义信息
 *  	2. 处理 Mapper 的 bean 定义信息 :
 *  		1. 为bean定义信息的 beanClass对象为 org.mybatis.spring.mapper.MapperFactoryBean，该对象又继承 SqlSessionDaoSupport 对象 又实现 Spring的 FactoryBean 接口
 *  		2. 为 bean 定义信息设置属性有:
 *  			- mapperInterface：你的具体Mapper接口 class对象
 *  			- sqlSessionFactory：你具体的 sqlSessionFactoryBean名称 或者是 sqlSessionFactory 对象
 *  				1. 在Spring 属性填充时候会为每个 Mapper对象 根据 sqlSessionFactoryBean名称 设置 sqlSessionFactory。
 *  				2. 在设置 sqlSessionFactory 属性值同时这步骤会创建一个 sqlSessionTemplate 对象（sqlSessionTemplate 继承 sqlSession ），在创建 sqlSessionTemplate 中 会创建一个 sqlSession 代理对象。
 *  				3. 最终 sqlSessionFactory 对象 会设置到 sqlSessionTemplate 对象中，所以对外暴露的是 sqlSessionTemplate 对象 而不是 sqlSessionFactory 对象，该步骤发生在 SqlSessionDaoSupport 对象方法中。
 *
 * 		3.通过上面的两步骤分析：可以知道 Mapper 对象的创建 是通过 MapperFactoryBean 来创建的， 而 MapperFactoryBean它实现Spring的 FactoryBean 接口。最后得出 Mapper对象创建是通过 FactoryBean 接口 getObject方法 创建
 *
 * 	经过以上分析，知道了一些脉络，接下来分析 FactoryBean 接口 getObject 方法怎么创建 Mapper对象：
 * 		1.它会获取 sqlSession 最后调用 getMapper 方法。传递你的 mapperInterface 接口class。而sqlSession对象实际是 sqlSessionTemplate 对象
 * 		2.通过 sqlSessionTemplate 获取 sqlSessionFactory 对象最后生成Mapper的代理对象	。这个 Mapper 代理对象的 sqlSession 属性就是 sqlSessionTemplate 对象。
 *
 * 	分析整个 Mapper 对象的创建 以及对象之间的关系，那么分析最后的调用流程：
 * 		调用 Mapper 对象的方法 会经过 Mapper对象代理 invoke方法，传递参数是 sqlSession ，最后调用 sqlSession 方法。此时我们也知道 这个 sqlSession 其实是 sqlSessionTemplate 对象。
 * 		进入 sqlSessionTemplate 里面有调用 sqlSessionProxy 代理对象方法 最后走到 SqlSessionInterceptor 的invoke方法中，然后从 sqlSessionFactory 获取真正的  sqlSession 对象，最终执行增删改查。
 *
 * 	简单总结： Mapper代理对象 ->  sqlSessionTemplate -> sqlSessionProxy（SqlSessionInterceptor）-> sqlSession

 *
 *
 *
 */

@Configuration
@ComponentScan(basePackages = "com.elite.xml.service")
@EnableTransactionManagement
public class MybatisConfig {

	@Bean
	public DataSource dataSource(){
		DriverManagerDataSource dataSource = new DriverManagerDataSource();

		dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
		dataSource.setUrl("jdbc:mysql://39.108.176.20:3306/test?serverTimezone=UTC");
		dataSource.setUsername("root");
		dataSource.setPassword("wwh3183..");
		return dataSource;
	}
	@Bean
	public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws MalformedURLException {
		SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
		sqlSessionFactoryBean.setDataSource(dataSource);
		sqlSessionFactoryBean.setConfigLocation(new FileUrlResource("D:\\spring-framework\\my-spring\\src\\main\\resources\\mybatis\\config\\mybatis-config.xml"));
		sqlSessionFactoryBean.setMapperLocations(new FileUrlResource("D:\\spring-framework\\my-spring\\src\\main\\resources\\mybatis\\mapper\\UserMapper.xml"));
		return sqlSessionFactoryBean;
	}
	@Bean
	public MapperScannerConfigurer mapperScannerConfigurer(){

		MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
		mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactoryBean");
		mapperScannerConfigurer.setBasePackage("com.elite.xml.mapper");
		return mapperScannerConfigurer;
	}

	@Bean
	public DataSourceTransactionManager dataSourceTransactionManager1(DataSource dataSource){
		DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
		dataSourceTransactionManager.setDataSource(dataSource);
		return dataSourceTransactionManager;
	}

//	@Bean
//	public DataSourceTransactionManager dataSourceTransactionManager2(DataSource dataSource){
//		DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
//		dataSourceTransactionManager.setDataSource(dataSource);
//		return dataSourceTransactionManager;
//	}

//	@Bean
//	public TransactionTemplate transactionTemplate(@Qualifier("dataSourceTransactionManager1") PlatformTransactionManager platformTransactionManager){
//		return new TransactionTemplate(platformTransactionManager);
//	}
}
